/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Simple module for storing and fetching message replies - implementation.
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#include "msgreplybuffer.h"

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

void msgreplybufferInit(TMsgReplyBuffer *b)
{
  memset(b, 0x00, sizeof(struct SMsgReplyBuffer));
  b->lock = ownMutex_Init();
  b->cond = ownCond_Init();
  idMapInit(&b->messageMap, 50);
}
//*****************************************************************************

void msgreplybufferDeinit(TMsgReplyBuffer *b)
{
  ownMutex_Destroy(b->lock);
  ownCond_Destroy(b->cond);
  idMapDeinit(&b->messageMap);
}
//*****************************************************************************

inline void msgreplybufferLock(TMsgReplyBuffer *b)
{
  ownMutex_Lock(b->lock);
}
//*****************************************************************************

inline void msgreplybufferUnlock(TMsgReplyBuffer *b)
{
  ownMutex_Unlock(b->lock);
}
//*****************************************************************************

int msgreplybufferInsertMsg(TMsgReplyBuffer *b, const uint32_t msgid, void *ptr)
{
  TMsgReplyBufferEntry *entry = malloc(sizeof(TMsgReplyBufferEntry));
  entry->signaled = 0;
  entry->message_id = msgid;
  entry->timestamp_s = time(NULL);
  entry->query = ptr;
  entry->reply = NULL;

  msgreplybufferLock(b);
  const int result = idMapAddEntry(&b->messageMap, msgid, entry);
  msgreplybufferUnlock(b);

  if (result) free(entry);

  return result;
}
//*****************************************************************************

int msgreplybufferWaitReply(TMsgReplyBuffer *b,
                            TMsgReplyBufferEntry *entry, 
                            const uint32_t msgid, 
                            const unsigned int timeout_ms)
{
  int result = -1;
  ownTime_ms_t begin = ownTime_get_ms();
  ownTime_ms_delta_t left = timeout_ms;
  const TIDMapEntry *idmentry = NULL;

  msgreplybufferLock(b);
  while(((idmentry = idMapGetEntry(&b->messageMap, msgid)) == NULL ||
         !((TMsgReplyBufferEntry*)(idmentry->entry))->signaled) &&
        left > 0) {
    ownCond_Wait(b->cond, b->lock, left);
    left = ownTime_get_ms_left(timeout_ms, begin);
  }
  if (idmentry) {
    TMsgReplyBufferEntry *mrbe = (TMsgReplyBufferEntry*)(idmentry->entry);
    if (mrbe->signaled) {
      memcpy(entry, idmentry->entry, sizeof(TMsgReplyBufferEntry));
      free(idmentry->entry);
      idMapDeleteEntry(&b->messageMap, msgid);
      result = 0;
    }
  }
  msgreplybufferUnlock(b);
  
  return result;
}
//*****************************************************************************

int msgreplybufferNotifyReply(TMsgReplyBuffer *b,
                              const uint32_t msgid, void *reply)
{
  int result = -1;

  msgreplybufferLock(b);
  const TIDMapEntry *idmentry = idMapGetEntry(&b->messageMap, msgid);
  if (idmentry) {
    TMsgReplyBufferEntry *mrbe = (TMsgReplyBufferEntry*)(idmentry->entry);
    mrbe->signaled = 1;
    mrbe->reply = reply;
    ownCond_Broadcast(b->cond);
    result = 0;
  }
  msgreplybufferUnlock(b);
  
  return result;
}
//*****************************************************************************

void msgreplybufferClear(TMsgReplyBuffer *b)
{
  TIDMapEntryIterator iter;
  TIDMapEntry *identry;
  idMapGetNextEntryIterator(&b->messageMap, &iter);
  while((identry=(TIDMapEntry *)idMapGetNextEntry(&b->messageMap, &iter))) {
    free(identry->entry);
  }
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
